//
// Copyright (C) 2008 Jackson Harper (jackson@ximian.com)

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
//

using System;
using System.Data;
using System.Configuration;
using System.Text.RegularExpressions;

using MySql.Data.MySqlClient;

namespace Grr.Model {

	[Serializable]
	public class GrurrahUser : DbEntity {

		private string friendly_name;
		private string email;
		private string location;

		private static readonly int MaxUsernameLength = 36;
		private static readonly int MaxFriendlyNameLength = 60;
		private static readonly int MaxEmailLength = 100;
		private static readonly int MaxLocationLength = 255;

		// for cereal
		public GrurrahUser() { }

		private GrurrahUser(string name) : base (name)
		{
		}

		public string FriendlyName {
			get { return friendly_name; }
		}

		public string Email {
			get { return email; }
		}

		public string Location {
			get { return location; }
		}
        
		public void SetFriendlyName (string friendly_name)
		{
			using (MySqlConnection con = OpenReadWriteConnection ()) {
				MySqlCommand cmd = new MySqlCommand ("UPDATE Users SET FriendlyName = ?friendly_name where Username = ?name", con);
				cmd.Parameters.Add ("?name", MySqlDbType.VarChar, MaxUsernameLength).Value = Name;
				cmd.Parameters.Add ("?friendly_name", MySqlDbType.VarChar, MaxFriendlyNameLength).Value = friendly_name;

				if (cmd.ExecuteNonQuery () != 1)
					throw new Exception ("Unable to set friendly name.");
				this.friendly_name = friendly_name;
			}
		}

		public void SetExtraPropertiesForName (string friendly_name, string location, string created_ip)
		{
			using (MySqlConnection con = OpenReadWriteConnection ()) {
                MySqlCommand cmd = new MySqlCommand("UPDATE Users SET FriendlyName = ?friendly_name, Location = ?location, creator_ip = INET_ATON(?creator_ip) where Username = ?name", con);
				cmd.Parameters.Add ("?name", MySqlDbType.VarChar, MaxUsernameLength).Value = Name;
				cmd.Parameters.Add ("?friendly_name", MySqlDbType.VarChar, MaxFriendlyNameLength).Value = friendly_name;
				cmd.Parameters.Add ("?location", MySqlDbType.VarChar, MaxLocationLength).Value = location;
                cmd.Parameters.Add("?creator_ip", MySqlDbType.String).Value = created_ip;

				if (cmd.ExecuteNonQuery () != 1)
				    throw new Exception ("Unable to set optional information.");
            
				this.friendly_name = friendly_name;
				this.location = location;
			}
		}

		public static GrurrahUser QueryForByName (string name)
		{
			using (MySqlConnection con = OpenReadConnection ()) {
				MySqlCommand cmd = new MySqlCommand ("select Username, Friendlyname, Email, Location from Users where Username = ?name", con);
				cmd.Parameters.Add ("?name", MySqlDbType.VarChar, MaxUsernameLength).Value = name;

				GrurrahUser res = RunQuery (cmd);
				return res;
			}
		}

		public static GrurrahUser QueryForByEmail (string email)
		{
			using (MySqlConnection con = OpenReadConnection ()) {
				MySqlCommand cmd = new MySqlCommand ("select Username, Friendlyname, Email, Location from Users where Email = ?email", con);
				cmd.Parameters.Add ("?email", MySqlDbType.VarChar, MaxEmailLength).Value = email;

				GrurrahUser res = RunQuery (cmd);
				return res;
			}
		}

		public static GrurrahUser QueryForByFriendlyName(string friendly_name)
		{
			using (MySqlConnection con = OpenReadConnection()) {
				MySqlCommand cmd = new MySqlCommand ("select Username, FriendlyName, Email, Location from Users where FriendlyName = ?friendly_name", con);
				cmd.Parameters.Add ("?friendly_name", MySqlDbType.VarChar, MaxFriendlyNameLength).Value = friendly_name;

				GrurrahUser res = RunQuery (cmd);
				return res;
			}
		}

        public static bool IsNameValid (string name, out string reason)
        {
            if (name == null) {
                reason = "Name is null";
                return false;
            }
            
            if (name.Length < 1) {
                reason = "Name must be at least two characters";
                return false;
            }

            bool has_data = false;
            foreach (char c in name)
            {
                if (Char.IsLetterOrDigit(c))
                {
                    has_data = true;
                    continue;
                }

                switch (c)
                {
                    case ' ':
                    case '_':
                    case '-':
                        break;
                    default:
                        reason = String.Format("Name contains invalid character ({0})", c);
                        return false;
                        break;
                }
            }

            if (!has_data)
            {
                reason = "Name must contain at least 1 letter or digit";
                return false;
            }

            reason = null;
            return true;
        }

        public static bool IsEmailValid(string email, out string reason)
        {
            string r = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$";
            
            Regex re = new Regex (r);
            if (re.IsMatch(email))
            {
                reason = null;
                return true;
            }
            
            reason = "Email address is not valid.";
            return false;
        }

        public static bool IsLocationValid(string location, out string reason)
        {
            if (location == null)
            {
                reason = "Location is null";
                return false;
            }

            foreach (char c in location)
            {
                if (Char.IsLetterOrDigit(c))
                    continue;

                switch (c)
                {
                    case ' ':
                    case '_':
                    case '-':
                    case ',':
                    case '.':
                        break;
                    default:
                        reason = String.Format("Location contains invalid character ({0})", c);
                        return false;
                        break;
                }
            }

            reason = null;
            return true;
        }

        public static bool IsPasswordValid(string pass, out string reason)
        {
            if (pass == null)
            {
                reason = "Password is null";
                return false;
            }

            if (pass.Length < 1)
            {
                reason = "Password must be at least one char";
                return false;
            }

            reason = null;
            return true;
        }

        private static GrurrahUser RunQuery (MySqlCommand cmd)
		{
			MySqlDataReader reader = cmd.ExecuteReader ();

			if (!reader.Read ())
				return null;

			GrurrahUser res = new GrurrahUser (reader.GetString("Username"));
			res.email = reader.GetString ("Email");
            
			if (reader ["FriendlyName"] != DBNull.Value)
				res.friendly_name = reader.GetString ("FriendlyName");

			if (reader ["Location"] != DBNull.Value)
				res.location = reader.GetString ("Location");

			return res;
		}
	}
}
